package gu.sql2java.transformer;

import java.lang.reflect.Type;
import java.util.Map;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import com.google.common.reflect.TypeToken;

/**
 * 预定义的转换器特征,实现了ContextProvider接口,提供转换器上下文
 * 
 * @author guyadong
 * @since 4.4.0
 */
public enum Feature implements ContextProvider {
	/**
	 * 默认转换器特征
	 */
	DEFAULT(TransformerContext.DEFAULT),
	/**
	 * 空值(空字符串，空容器对象，空数组等)转换为空值(空字符串，空容器对象，空数组等)，则返回{@code null}
	 */
	EMPTY_TO_NULL(new TransformerContext() {
		@Override
		public <T> Supplier<T> placeholderOnEmpty(BaseTypeTransformer typeTransformer, TypeToken<T> valueType) {
			return Suppliers.ofInstance(null);
		}
	}),
	/**
	 * {@code null}转换为空值(空字符串，空容器对象，空数组等)，如果不是可为空的类型，则返回{@code null}
	 * @see BaseTypeTransformer#emptyOf(TypeToken, boolean)
	 */
	NULL_TO_EMPTY(new TransformerContext() {
		@Override
		public <T> Supplier<T> placeholderOnNull(BaseTypeTransformer typeTransformer, TypeToken<T> valueType) {
			return Suppliers.ofInstance(typeTransformer.emptyOf(valueType, false));
		}
	}),
	/**
	 * 用于在输入为null时为基本类型(primitive type)提供默认值
	 */
	PRIMITIVE_PLACEHOLDER(new TransformerContext() {
		final ImmutableMap<Type, Object> PRIMITIVES_PLACEHOLDER = ImmutableMap.<Type, Object>builder()
				.put(boolean.class, false).put(byte.class, (byte) 0).put(short.class, (short) 0).put(int.class, 0)
				.put(long.class, 0L).put(float.class, 0.0f).put(double.class, 0.0).put(char.class, '\0').build();

		@SuppressWarnings("unchecked")
		@Override
		public <T> Supplier<T> placeholderOnNull(BaseTypeTransformer typeTransformer, TypeToken<T> valueType) {
			if (valueType.isPrimitive()) {
				return Suppliers.ofInstance((T)  PRIMITIVES_PLACEHOLDER.get(valueType.getRawType()));
			}
			return super.placeholderOnNull(typeTransformer, valueType);
		}
	}),
	/**
	 * 忽略容器对象(集合，数组，Map等)中的{@code null}元素
	 */
	IGNORE_NULL_ELEMENT(new TransformerContext() {
		@SuppressWarnings({ "rawtypes" })
		@Override
		public <T, E> Predicate<T> elementFilter(BaseTypeTransformer typeTransformer, TypeToken<E> elementType) {
			if (elementType.isSubtypeOf(Map.Entry.class)) {
				return new Predicate<T>() {
					@Override
					public boolean apply(T input) {
						if (input instanceof Map.Entry) {
							Map.Entry entry = (Map.Entry) input;
							return entry.getKey() != null && entry.getValue() != null;
						}
						return false;
					}
				};
			}
			return Predicates.notNull();
		}
	}),
	/**
	 * 忽略容器对象(集合，数组，Map等)中的{@code null}或空元素(空字符串，空容器对象，空数组等)
	 */
	IGNORE_EMPTY_ELEMENT(new TransformerContext() {
		private final Predicate<Object> notEmpty = new Predicate<Object>() {
			@Override
			public boolean apply(Object input) {
				return !BaseTypeTransformer.isEmpty(input);
			}
		};
		private final Predicate<Object> notEmptyEntry = new Predicate<Object>() {
			@SuppressWarnings("rawtypes")
			@Override
			public boolean apply(Object input) {
				if (input instanceof Map.Entry) {
					Map.Entry entry = (Map.Entry) input;
					return !BaseTypeTransformer.isEmpty(entry.getKey()) && !BaseTypeTransformer.isEmpty(entry.getKey());
				}
				return false;
			}
		};
		@SuppressWarnings("unchecked")
		@Override
		public <T, E> Predicate<T> elementFilter(BaseTypeTransformer typeTransformer, TypeToken<E> elementType) {
			if (elementType.isSubtypeOf(Map.Entry.class)) {
				return (Predicate<T>) notEmptyEntry;
			}
			return (Predicate<T>) notEmpty;
		}
	}),
	/**
	 * 当转换器转换失败时，返回{@code null}
	 */
	NULL_ON_TRANSFORM_ERROR(new TransformerContext() {
		private final OnTransformError<?, Throwable> NULL_ON_TRANSFORM_ERROR = new OnTransformError<Object, Throwable>() {

			@Override
			public <F> Object onThrow(F f, Throwable e) throws Throwable {
				return null;
			}
		};
		@SuppressWarnings("unchecked")
		@Override
		public <T, E extends Throwable> OnTransformError<T, E> functionOnTransformError(BaseTypeTransformer typeTransformer, TypeToken<T> valueType) {
			return (OnTransformError<T, E>) NULL_ON_TRANSFORM_ERROR;
		}
	});

	private final TransformerContext ctx;

	private Feature(TransformerContext ctx) {
		this.ctx = ctx;
	}

	@Override
	public TransformerContext getContext() {
		return ctx;
	}
}
